{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Advanced street network plotting with OSMnx\n",
    "\n",
    "Author: [Geoff Boeing](https://geoffboeing.com/)\n",
    "\n",
    "  - [Documentation](https://osmnx.readthedocs.io/)\n",
    "  - [Journal article and citation info](https://geoffboeing.com/publications/osmnx-paper/)\n",
    "  - [Code repository](https://github.com/gboeing/osmnx)\n",
    "  - [Examples gallery](https://github.com/gboeing/osmnx-examples)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "import osmnx as ox\n",
    "\n",
    "ox.__version__"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "place = \"Piedmont, California, USA\"\n",
    "G = ox.graph.graph_from_place(place, network_type=\"drive\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Color helper functions\n",
    "\n",
    "You can use the plot module to get colors for plotting."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# get n evenly-spaced colors from some matplotlib colormap\n",
    "ox.plot.get_colors(n=5, cmap=\"plasma\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# get node colors by linearly mapping an attribute's values to a colormap\n",
    "nc = ox.plot.get_node_colors_by_attr(G, attr=\"y\", cmap=\"plasma\")\n",
    "fig, ax = ox.plot.plot_graph(G, node_color=nc, edge_linewidth=0.3)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# when num_bins is not None, bin the nodes/edges then assign one color to each bin\n",
    "# also set equal_size=True for equal-sized quantiles (requires unique bin edges!)\n",
    "ec = ox.plot.get_edge_colors_by_attr(G, attr=\"length\", num_bins=5)\n",
    "\n",
    "# otherwise, when num_bins is None (default), linearly map one color to each node/edge by value\n",
    "ec = ox.plot.get_edge_colors_by_attr(G, attr=\"length\")\n",
    "\n",
    "# plot the graph with colored edges\n",
    "fig, ax = ox.plot.plot_graph(G, node_size=5, edge_color=ec, bgcolor=\"k\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Other plotting options\n",
    "\n",
    "See the [documentation](https://osmnx.readthedocs.io/en/stable/osmnx.html#osmnx.plot.plot_graph) for full details."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "fig, ax = ox.plot.plot_graph(\n",
    "    G,\n",
    "    ax=None,  # optionally draw on pre-existing axis\n",
    "    figsize=(8, 8),  # figure size to create if ax is None\n",
    "    bgcolor=\"#111111\",  # background color of the plot\n",
    "    node_color=\"w\",  # color of the nodes\n",
    "    node_size=15,  # size of the nodes: if 0, skip plotting them\n",
    "    node_alpha=None,  # opacity of the nodes\n",
    "    node_edgecolor=\"none\",  # color of the nodes' markers' borders\n",
    "    node_zorder=1,  # zorder to plot nodes: edges are always 1\n",
    "    edge_color=\"#999999\",  # color of the edges\n",
    "    edge_linewidth=1,  # width of the edges: if 0, skip plotting them\n",
    "    edge_alpha=None,  # opacity of the edges\n",
    "    show=True,  # if True, call pyplot.show() to show the figure\n",
    "    close=False,  # if True, call pyplot.close() to close the figure\n",
    "    save=False,  # if True, save figure to disk at filepath\n",
    "    filepath=None,  # if save is True, the path to the file\n",
    "    dpi=300,  # if save is True, the resolution of saved file\n",
    "    bbox=None,  # bounding box to constrain plot\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Use bbox to constrain the plot (i.e., \"zoom\") to some precise section of the graph.\n",
    "\n",
    "For example, perhaps we consolidated nearby intersections to clean-up node clusters and want to inspect our results:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "Gc = ox.simplification.consolidate_intersections(ox.projection.project_graph(G), dead_ends=True)\n",
    "c = ox.convert.graph_to_gdfs(G, edges=False).union_all().centroid\n",
    "bbox = ox.utils_geo.bbox_from_point(point=(c.y, c.x), dist=200, project_utm=True)\n",
    "fig, ax = ox.plot.plot_graph(\n",
    "    Gc,\n",
    "    figsize=(5, 5),\n",
    "    bbox=bbox,\n",
    "    edge_linewidth=2,\n",
    "    edge_color=\"c\",\n",
    "    node_size=80,\n",
    "    node_color=\"#222222\",\n",
    "    node_edgecolor=\"c\",\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# or save a figure to disk instead of showing it\n",
    "fig, ax = ox.plot.plot_graph(G, filepath=\"./images/image.png\", save=True, show=False, close=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Plot routes"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# impute missing edge speeds and calculate free-flow travel times\n",
    "G = ox.routing.add_edge_speeds(G)\n",
    "G = ox.routing.add_edge_travel_times(G)\n",
    "\n",
    "# calculate 3 shortest paths, minimizing travel time\n",
    "w = \"travel_time\"\n",
    "orig, dest = list(G)[10], list(G)[-10]\n",
    "route1 = ox.routing.shortest_path(G, orig, dest, weight=w)\n",
    "orig, dest = list(G)[0], list(G)[-1]\n",
    "route2 = ox.routing.shortest_path(G, orig, dest, weight=w)\n",
    "orig, dest = list(G)[-100], list(G)[100]\n",
    "route3 = ox.routing.shortest_path(G, orig, dest, weight=w)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Plot a route with the `plot_graph_route` function."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "fig, ax = ox.plot.plot_graph_route(G, route1, orig_dest_size=0, node_size=0)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# you can also pass any ox.plot_graph parameters as additional keyword args\n",
    "fig, ax = ox.plot.plot_graph_route(G, route1, save=True, show=False, close=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Or plot multiple routes with the `plot_graph_routes` function.\n",
    "\n",
    "If you provide a list of route colors, each route will receive its own color."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "routes = [route1, route2, route3]\n",
    "rc = [\"r\", \"y\", \"c\"]\n",
    "fig, ax = ox.plot.plot_graph_routes(G, routes, route_colors=rc, route_linewidth=6, node_size=0)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Annotate a graph's plot\n",
    "\n",
    "Here we label each street segment with its name. Similar logic would apply to labeling nodes instead."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "G2 = ox.graph.graph_from_address(\"Piedmont, CA, USA\", dist=200, network_type=\"drive\")\n",
    "G2 = ox.convert.to_undirected(G2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "fig, ax = ox.plot.plot_graph(G2, edge_linewidth=3, node_size=0, show=False, close=False)\n",
    "for _, edge in ox.convert.graph_to_gdfs(G2, nodes=False).fillna(\"\").iterrows():\n",
    "    text = edge[\"name\"]\n",
    "    c = edge[\"geometry\"].centroid\n",
    "    ax.annotate(text, (c.x, c.y), c=\"y\")\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Plot footprints\n",
    "\n",
    "The `plot_footprints` function lets you plot OSM geospatial features (Polygons and MultiPolygons)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# get all the building footprints in a city\n",
    "gdf = ox.features.features_from_place(\"Piedmont, California, USA\", {\"building\": True})\n",
    "gdf.shape"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "fig, ax = ox.plot.plot_footprints(gdf)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# or plot street network and the geospatial features' footprints together\n",
    "fig, ax = ox.plot.plot_footprints(gdf, alpha=0.4, show=False)\n",
    "fig, ax = ox.plot.plot_graph(G, ax=ax, node_size=0, edge_color=\"w\", edge_linewidth=0.7)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.8"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
